package com.ruoyi.trackapply;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.biz.BizModule;
import com.ruoyi.common.utils.biz.PathUtil;
import com.ruoyi.common.utils.biz.ResultSaveModule;
import com.ruoyi.modelbuild.service.ModelAdjustService;
import com.ruoyi.system.domain.ResultSave;
import com.ruoyi.system.service.IResultSaveService;
import com.ruoyi.trackapply.domain.CloseEarthParam;
import com.ruoyi.trackapply.domain.CloseEarthResBO;
import com.ruoyi.trackapply.domain.CloseEarthVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 近地 深空航天器轨道大气模型评估
 */

@Slf4j
@Service
public class CloseEarthService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    ModelAdjustService adjustService;

    @Autowired
    private IResultSaveService resultSaveService;

    /**
     * 评估
     * @param param
     * @return
     */
    public CloseEarthVO estimate(CloseEarthParam param) throws Exception {
        CloseEarthResBO resBO = exec(param);
        CloseEarthVO closeEarthVO = new CloseEarthVO();

        //折线图
        String csvPath = resBO.getCsvPath();
        CloseEarthVO.ChartVO chartVO = closeEarthVO.new ChartVO();
        chartVO  = dealWithRes(csvPath,chartVO);
        closeEarthVO.setChart1(chartVO);

        //评估表
        List<CloseEarthVO.EstimateVO> estimateVOS = new ArrayList<>();
        CloseEarthVO.EstimateVO jb = closeEarthVO.new EstimateVO();
        jb.setModelName("JB2008模拟值");
        jb.setMse(resBO.getJb08ModelMse());
        jb.setMae(resBO.getJb08ModelMae());
        jb.setR(resBO.getJb08ModelR2());
        estimateVOS.add(jb);

        CloseEarthVO.EstimateVO opt = closeEarthVO.new EstimateVO();
        opt.setModelName("修正后");
        opt.setMse(resBO.getOptModelMse());
        opt.setMae(resBO.getOptModelMae());
        opt.setR(resBO.getOptModelR2());
        estimateVOS.add(opt);

        CloseEarthVO.EstimateVO weight = closeEarthVO.new EstimateVO();
        weight.setModelName("加权融合后");
        weight.setMse(resBO.getWeightedModelMse());
        weight.setMae(resBO.getWeightedModelMae());
        weight.setR(resBO.getWeightedModelR2());
        estimateVOS.add(weight);

        closeEarthVO.setTableData(estimateVOS);
        return closeEarthVO;
    }


    private CloseEarthVO.ChartVO dealWithRes(String csvPath, CloseEarthVO.ChartVO chartVO) throws Exception {

        BufferedReader reader = new BufferedReader(new FileReader(csvPath));
        long lines = Files.lines(Paths.get(csvPath)).count() - 1;
        //页面展示1000条数据
        int num = 2000;
        int lineNum = 0;
        int skip = (int) (lines / num);
        if (lines <num) {
            skip =1 ;
        }
        //首行忽略
        reader.readLine();
        String line = "";
        List<String>  timeList = new ArrayList<>();
        List<BigDecimal> obsList = new ArrayList<>();
        List<BigDecimal> adjustList = new ArrayList<>();
        List<BigDecimal> jb08List = new ArrayList<>();
        List<BigDecimal> weightList = new ArrayList<>();
        while ((line = reader.readLine())!= null) {
            if (lineNum % skip == 0) {
                String[] split = line.split(",");
                //TIME,JB08_DENSITY,MODEL_DENSITY,OBS_DENSITY,Weighted_Model
                //2003-01-01 00:00:02,4.434969407474044e-13,1.1936157030947712e-11,4.4205237e-13,4.916844795610943e-13
                timeList.add(split[0]);
                jb08List.add(new BigDecimal(split[1]));
                adjustList.add(new BigDecimal(split[2]));
                obsList.add(new BigDecimal(split[3]));
                weightList.add(new BigDecimal(split[4]));
            }
            lineNum++;
        }
        chartVO.setAdjust(adjustList);
        chartVO.setTime(timeList);
        chartVO.setWeight(weightList);
        chartVO.setJb08(jb08List);
        chartVO.setObs(obsList);
        return chartVO;
    }

    /**
     * 结果入库
     * @param param
     */
    public void save(CloseEarthParam param) {
        CloseEarthResBO execRes = exec(param);
        ResultSave entity = ResultSave.builder()
                .params("")
                .module(ResultSaveModule.TRACK_APPLY)
                .submodule(ResultSaveModule.TRACK_APPLY_DEEP)
                .resultData(JSONUtil.toJsonStr(ListUtil.of(execRes.getCsvPath())))
                .startTime(DateUtil.format(param.getDenStartTime(),"yyyy-MM-dd"))
                .endTime(DateUtil.format(param.getDenEndTime(),"yyyy-MM-dd"))
                .createTime(new Date())
                .build();
        resultSaveService.resultSave(param,entity);
    }

    /**
     * 下载
     * @param param
     * @return
     */
    public File download(CloseEarthParam param) {
        CloseEarthResBO execRes = exec(param);

        String zipPath = PathUtil.tmpModuleDir(BizModule.TRACK_APPLY) + "/" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".zip";
        File zip = ZipUtil.zip(execRes.getCsvPath(), zipPath, false);
        return zip;
    }


    //执行评估
    private CloseEarthResBO exec(CloseEarthParam param) {

        String key = MD5.create().digestHex(param.toString());
        Object cacheObj = redisTemplate.opsForValue().get(key);
        if (cacheObj != null) {
            return JSONUtil.toBean((String) cacheObj,CloseEarthResBO.class);
        }

        //1 找到最匹配的系数文件
        String jsonFile = adjustService.findParam(param.getTempSate(),param.getTempStartTime(),param.getTempEndTime());

        //2组装参数
        List<String> commonds = new ArrayList<>();
        commonds.add("python");
        String script = PathUtil.codeModuleDir(BizModule.TRACK_APPLY) + PathUtil.moduleDirs().getTrackApply().get("closeEarthAssess");
        commonds.add(script);

        commonds.add("-sn");
        commonds.add(param.getDenSate());

        //修正系数
        commonds.add("-fp");
        commonds.add(jsonFile);

        //时间
        String start = DateUtil.format(param.getDenStartTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        String end = DateUtil.format(param.getDenEndTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);

        commonds.add("-sd");
        commonds.add(start);
        commonds.add("-ed");
        commonds.add(end);

        //输出参数 json,csv和日志
        String tmp = PathUtil.tmpModuleDir(BizModule.TASK_ASSESS) + "/" + DateUtil.format(new Date(), "yyyyMMdd");
        new File(tmp).mkdirs();
        String uuid = UUID.fastUUID().toString();
        commonds.add("-cp");
        commonds.add(tmp+"/"+uuid+".json");
        commonds.add("-wjp");
        commonds.add(tmp+"/"+uuid+".csv");
        commonds.add("-lp");
        commonds.add(tmp+"/"+uuid+".log");

        String execCommond = CollectionUtil.join(commonds, " ");
        log.info("执行命令:{}",execCommond);
        String res = RuntimeUtil.execForStr(execCommond);
        log.info("执行结果:{}",res);
        CloseEarthResBO resObj = JSONUtil.toBean(res,CloseEarthResBO.class);
        if (resObj.getCode() ==1) {
            redisTemplate.opsForValue().set(key,res,1,TimeUnit.HOURS);
            return resObj;
        }else {
            log.error("执行结果:{}",res);
            throw new ServiceException("脚本执行异常:"+resObj.getMsg());
        }
    }
}
